#include "CameraCounter.h"
#include "Path.h"

#include "FileVideoSource.h"
#include "DirVideoSource.h"
#include "VideoCaptureSource.h"

#include "AppConfig.h"
#include "TStringUtil.h"

#include <QMessageBox>




CameraCounter::CameraCounter(QObject *parent) :
    QObject(parent),
    m_timerId(),
    m_count(),
    m_rate(3),
    m_videoLabel(),
    m_statusWidget(),
	m_counterThread()
{
}

void CameraCounter::setupUI(QLabel* videoLabel, HistogramWidget* statusWidget)
{
    m_videoLabel = videoLabel;
    m_statusWidget = statusWidget;
}

void CameraCounter::setCamera(Camera::Device& dev)
{
    m_device = dev;
}

const Camera::Device& CameraCounter::getCamera() const
{
    return m_device;
}

void CameraCounter::start()
{
    stop();

    loadCarriageValueConfig();
    buildVideoSource();

    if (!m_videoSource->isOpened())
    {
        QMessageBox::warning(nullptr, "failed to open camera", m_device.name.c_str());
        m_videoSource.release();
        return;
    }

    int duration = 50;
    if (m_rate > 0 && m_rate <= 30)
    {
        duration = 1000 / m_rate;
    }

	m_counterThread = new CounterThread(*this, duration);
	connect(m_counterThread, &CounterThread::counterDone, this, &CameraCounter::onCounterDone);
	connect(m_counterThread, &CounterThread::finished, m_counterThread, &QObject::deleteLater);
	m_counterThread->start();

    //m_timerId = startTimer(duration);

    int port = AppConfig::getInt("App/HttpPort", 18800);
    port += (m_device.index + 1)* 2;
    m_caster.start(port);
}

void CameraCounter::stop()
{
	if (m_counterThread)
	{
		m_counterThread->stop();
		m_counterThread->quit();
		m_counterThread->wait();
		m_counterThread = nullptr;
	}

    if (m_timerId)
    {
        killTimer(m_timerId);
        m_timerId = 0;
    }

    m_videoSource.release();

    if (m_caster.isStarted())
    {
        m_caster.stop();
    }
}

void CameraCounter::timerEvent(QTimerEvent *event)
{
    handleFrameTimer();
}

void CameraCounter::handleFrameTimer()
{
    if (!m_videoSource)
    {
        return;
    }

    cv::Mat inFrame;
    m_videoSource->getFrame(inFrame);

    if (inFrame.empty())
    {
        return;
    }

    int count = 0;
    cv::Mat outFrame = m_headCounter.countHead(inFrame, count);
    showImage(outFrame);

    showStatus(count);

    //if (count != m_count)
    {
        broadcast(outFrame, count);
    }

    m_count = count;
}

void CameraCounter::showImage(cv::Mat& mat)
{
    cv::Mat rgb;
    cv::cvtColor(mat, rgb, CV_RGB2BGR);

    showImage(rgb, m_videoLabel);
}

void CameraCounter::showImage(cv::Mat& mat, QLabel* label)
{
    QImage img;
    if (mat.channels() == 3)
    {
        cv::Mat& rgb(mat);
        img = QImage((const uchar*)(rgb.data), rgb.cols, rgb.rows, rgb.cols*rgb.channels(), QImage::Format_RGB888);
    }
    else
    {
        img = QImage((const uchar*)(mat.data), mat.cols, mat.rows, mat.cols*mat.channels(), QImage::Format_Indexed8);
    }

    label->setPixmap(QPixmap::fromImage(img));
    label->show();
}

HeadCounter::Params& CameraCounter::counterParams()
{
    return m_headCounter.m_params;
}

const HeadCounter::Params& CameraCounter::counterParams() const
{
    return m_headCounter.m_params;
}

void CameraCounter::setRate(int fps)
{
    m_rate = fps;
}

void CameraCounter::buildVideoSource()
{
    std::string workdir = comn::Path::getWorkDir();
    std::string assetsDir = comn::Path::join(workdir, "..\\assets");

    //assetsDir = "C:\\Users\\chuan\\Documents\\WeChat Files\\door17\\Files";

#ifdef _DEBUG
    DirVideoSource* source = new DirVideoSource(assetsDir);
#else
    VideoCaptureSource* source = new VideoCaptureSource(m_device.index);
#endif //

    m_videoSource.reset(source);

    m_videoSource->setSize(cv::Size(640, 480));
}

void CameraCounter::broadcast(cv::Mat& mat, int count)
{
    //std::vector<uchar> buf;
    //cv::imencode(".jpg", mat, buf);

    //m_caster.sendBinary(&(buf[0]), buf.size());

    std::string text = comn::StringUtil::format("{\"count\": %d}", count);
    m_caster.sendText(text);
}

void CameraCounter::loadCarriageValueConfig()
{
    int index = m_device.index + 1;
    std::string filename = comn::StringUtil::format("%d.ini", index);
    std::string workdir = comn::Path::getWorkDir();
    std::string filepath = comn::Path::join(workdir, filename);

    m_carriageValue.load(filepath.c_str());

    m_exceptionChecker.setThreshold(m_carriageValue.exception, 2000);
}

void CameraCounter::showStatus(int count)
{
    CountExceptionChecker::TimedCount timedCount;
    bool changeException = m_exceptionChecker.check(count, timedCount);
    CarriageValue::Status status = m_carriageValue.getStatus(count);

    emit countResult(m_device.index, count, status, changeException);

    double scale = getStatusScale(status);

    QColor color = getStatusColor(status);
    m_statusWidget->setColor(color);
    m_statusWidget->setScale(scale);
}

QColor CameraCounter::getStatusColor(int status)
{
    if (status == CarriageValue::kEmpty)
    {
        return QColor(0, 0, 255);
    }
    else if (status == CarriageValue::kFull)
    {
        return QColor(255, 255, 0);
    }
    else if (status == CarriageValue::kCrowd)
    {
        return QColor(255, 128, 0);
    }
    return QColor(255, 0, 0);
}

double CameraCounter::getStatusScale(int status)
{
	if (status == CarriageValue::kEmpty)
	{
		return 0.2;
	}
	else if (status == CarriageValue::kFull)
	{
		return 0.5;
	}
	else if (status == CarriageValue::kCrowd)
	{
		return 0.8;
	}
	return 1.0;
}

void CameraCounter::processCount()
{
	if (!m_videoSource)
	{
		return;
	}

	cv::Mat inFrame;
	m_videoSource->getFrame(inFrame);

	if (inFrame.empty())
	{
		return;
	}

	int count = 0;
	cv::Mat outFrame = m_headCounter.countHead(inFrame, count);

	{
		comn::AutoCritSec lock(m_cs);
		m_outFrame = outFrame;
		m_count = count;
	}
}

void CameraCounter::onCounterDone()
{
	cv::Mat rgb;
	{
		comn::AutoCritSec lock(m_cs);
		cv::cvtColor(m_outFrame, rgb, CV_RGB2BGR);
	}
	
	showImage(rgb, m_videoLabel);

	showStatus(m_count);
}